/**
 * @fileOverview
 * WashizuMahjong.
 * http://code.google.com/p/washizu-mahjong/
 */
/**
 * @namespace
 */
var Score = new function(){
	var automatically_awarded_fu = 20;
	/**
	 * count 符
	 * @param	{HandInfo}	agari_handinfo	The handinfo of Agari hand.
	 * @param	{HandInfo[]}	ready_hand_detail	The details of the hand without agari tile.
	 * @param	{Array[]}	[playermeld=[]]	The meld of the owner of the hand
	 * @param	{Boolean}	self_draw
	 * @param	{Boolean}	isRinshan	whether the agari tile is rinshan tile
	 * @param	{Boolean}	agari_from_wall Whether the agari tile is from wall.
	 * @param	{Number}	playerWind	the wind of the player. It is used for checking the Honor tiles in the pair.
	 */
	function _fu(agari_handinfo, ready_hand_detail, playermeld, self_draw, isRinshan, agari_from_wall, playerWind){
		if(!agari_handinfo || !(ready_hand_detail instanceof Array))
			return;
		//special yaku
		if(agari_handinfo.pairs.length == 7){
			// 7 pairs
			return Special_Fu_Value.Seven_Pairs;
		}
		var f = automatically_awarded_fu;
		if(!(playermeld instanceof Array))
			playermeld = [];
		var is_door_clear = true;
		for(var i = 0; i < playermeld.length; i++){
			if(playermeld[i][0] == "KONG"){
				if(playermeld[i].kongType == KongType.Open_Quad || playermeld[i].kongType == KongType.Added_Open_Quad){
					is_door_clear = false;
					break;
				}
			}else{
				is_door_clear = false;
				break;
			}
		}

		//The fu from meld
		for(i = 0; i < playermeld.length; i++){
			var m = playermeld[i];
			var t = playermeld[i].takedTile;
			switch(m.type){
				case MeldType.PONG:
					if(t.isYaoChuuTile){
						f += 4;
					}else{
						f += 2;
					}
					break;
				case MeldType.KONG:
					if(m.kongType == KongType.Open_Quad || m.kongType == KongType.Added_Open_Quad){
						if(t.typeCode == 3 || t.simpleNum == 1 || t.simpleNum == 9){
							f += 16;
						}else{
							f += 8;
						}
					}else{
						if(t.typeCode == 3 || t.simpleNum == 1 || t.simpleNum == 9){
							f += 32;
						}else{
							f += 16;
						}
					}
					break;
			}
		}


		//the fu from hand
		agari_handinfo.concealedTriplets.forEach(function(val, index, arr){
			if(val[0].isYaoChuuTile){
				f += 8;
			}else{
				f += 4;
			}
		});

		//the fu form pairs
		if(agari_handinfo.pairs[0][0].typeCode == MahjongType.Word){
			switch(agari_handinfo.pairs[0][0].wordNum){
				case MahjongWordNumCode.Haku:
					f += 2;
					break;
				case MahjongWordNumCode.Hatsu:
					f += 2;
					break;
				case MahjongWordNumCode.Chun:
					f += 2;
					break;
				case RoundDetail.code:
					// 場風
					f += 2;
				case playerWind:
					// 自風
					f += 2;
					break;
			}
		}

		//the fu of waiting tiles
		if(ready_hand_detail.length == 1){
			var r = ready_hand_detail[0];
			if(r.unused.length == 1 && r.unused[0] instanceof Array){
				if(r.unused[0][0].simpleNum == 1 || r.unused[0][1].simpleNum == 9){
					//辺張待ち
					f += 2;
				}
			}else{
				//嵌張待ち / 単騎待ち
				f += 2;
			}
		}

		//the fu from self draw (if it is 平和 or 嶺上開花, it won't be counted)
		if(is_door_clear && self_draw && !isRinshan && f != automatically_awarded_fu){
			f += 2;
		}

		if(is_door_clear && !self_draw){
			f += 10;
		}

		f = Math.ceil(f / 10) * 10;
		return f;
	}
	/**
	 * @function
	 * @see Score-normalScorePoint
	 */
	this.scorePoint = function(/** Number */fu,/** Number */ han){
		var result = fu * Math.pow(2,han+2);
		if(Rules.Aotenjou)
			return result;
		else
			return normalScorePoint(result, han);
	}
	/**
	 * @function
	 * @param {Number} point	the score
	 * @param {Number} han	the number of 飜
	 * @see Score.scorePoint
	 */
	function normalScorePoint(point, han){
		if(point < 2000)
			return point;
		switch(han){
			case 1:
			case 2:
			case 3:
			case 4:
			case 5:
				//満貫（まんがん）
				return 2000;
			case 6:
			case 7:
				//跳満（はねまん）
				return 3000;
			case 8:
			case 9:
			case 10:
				//倍満（ばいまん）
				return 4000;
			case 11:
			case 12:
				//三倍満（さんばいまん）
				return 6000;
			default:
				//累計役滿	/	数え役満	/	counted yakuman
				return 8000;
		}
	};
	/**
	 * Checking whether the winning player makes sacred discard（振聴／フリテン）.
	 * @see Score.yakuList
	 * @param {HandInfo[]} readyHandResults
	 * @param {MahjongTile} tile The agari tile.
	 * @param {MahjongTile[]} discardTiles	The discard tiles of the player.
	 * @return {Boolean} If it return true, means the player makes
	 */
	function sacredDiscardChecking(readyHandResults, tile, discardTiles){
		//TODO 目前已經完成普通振聽，未完成其他
		//TODO 為完成振聽，需要能判斷是否見逃
		//TODO 三麻拔北
		if(!(winner instanceof Player) || !(tile instanceof MahjongTile) || !(discardTiles instanceof Array)){
			return false;
		}
		var waitingTiles = [];
		readyHandResults.forEach(function(r){
			if(r.unused.length == 1 && (r.unused[0] instanceof MahjongTile)){
				//一張牌單騎
				waitingTiles.push(r.unused[0]);
			}else if(r.unused.length == 1 && (r.unused[0] instanceof Array)){
				// 順子缺一
				var unusedTiles = r.unused[0];
				if(unusedTiles[1].simpleNum - unusedTiles[0].simpleNum == 1){
					if(unusedTiles[0].simpleNum == 1){
						var m = new MahjongTile;
						m.typeCode = unusedTiles[0].typeCode;
						m.simpleNum = unusedTiles[1].simpleNum + 1;
						waitingTiles.push(m);
					}else if(unusedTiles[1].simpleNum == 9){
						var m = new MahjongTile;
						m.typeCode = unusedTiles[0].typeCode;
						m.simpleNum = unusedTiles[0].simpleNum - 1;
						waitingTiles.push(m);
					}else{
						var m1 = new MahjongTile, m2 = new MahjongTile;
						m1.typeCode = unusedTiles[0].typeCode;
						m2.typeCode = unusedTiles[0].typeCode;
						m1.simpleNum = unusedTiles[1].simpleNum + 1;
						m2.simpleNum = unusedTiles[0].simpleNum - 1;
						waitingTiles.push(m1);
						waitingTiles.push(m2);
					}
				}else if(unusedTiles[1].simpleNum - unusedTiles[0].simpleNum == 2){
					var m = new MahjongTile;
					m.typeCode = unusedTiles[0].typeCode;
					m.simpleNum = unusedTiles[0].simpleNum + 1;
					waitingTiles.push(m);
				}
			}else if(r.pairs.length == 2){
				// 雙碰聽
				waitingTiles.push(r.pairs[0][0])
				waitingTiles.push(r.pairs[1][0])
			}else if(r.unused.length == 11 && r.pairs.length == 1){
				var checkingList = [], i;
				for(i = 0; i < 13; i++){
					var m = new MahjongTile;
					switch(i){
						case 0:
							m.typeCode = MahjongType.Word;
							m.wordNum = MahjongWordNumCode.East;
							break;
						case 1:
							m.typeCode = MahjongType.Word;
							m.wordNum = MahjongWordNumCode.South;
							break;
						case 2:
							m.typeCode = MahjongType.Word;
							m.wordNum = MahjongWordNumCode.West;
							break;
						case 3:
							m.typeCode = MahjongType.Word;
							m.wordNum = MahjongWordNumCode.North;
							break;
						case 4:
							m.typeCode = MahjongType.Word;
							m.wordNum = MahjongWordNumCode.Haku;
							break;
						case 5:
							m.typeCode = MahjongType.Word;
							m.wordNum = MahjongWordNumCode.Hatsu;
							break;
						case 6:
							m.typeCode = MahjongType.Word;
							m.wordNum = MahjongWordNumCode.Chun;
							break;
						case 7:
							m.typeCode = MahjongType.Pin;
							m.simpleNum = 1;
							break;
						case 8:
							m.typeCode = MahjongType.Pin;
							m.simpleNum = 9;
							break;
						case 9:
							m.typeCode = MahjongType.Wan;
							m.simpleNum = 1;
							break;
						case 10:
							m.typeCode = MahjongType.Wan;
							m.simpleNum = 9;
							break;
						case 11:
							m.typeCode = MahjongType.Sou;
							m.simpleNum = 1;
							break;
						case 12:
							m.typeCode = MahjongType.Sou;
							m.simpleNum = 9;
							break;
					}
					m.typeCode = unusedTiles[0].typeCode;
					m.simpleNum = unusedTiles[0].simpleNum + 1;
					checkingList.push(m);
				}
				for(i = 0; i < r.unused.length; i++){
					for(var j = 0; j < checkingList.length; j++){
						if(checkingList[j].isSameTile(r.unused[i])){
							checkingList.removeByIndex(j);
							break;
						}
					}
				}
				for(var i = 0; i < checkingList.length; i++){
					if(checkingList[i].isSameTile(r.pairs[0])){
						checkingList.removeByIndex(i);
						break;
					}
				}
				checkingList.forEach(function(val){
					waitingTiles.push(val);
				});
			}else if(r.unused.length == 13 && r.pairs.length == 0){
				// 国士無双十三面
				r.unused.forEach(function(val){
					waitingTiles.push(val);
				});
			}
		});
		for(var i = 0; i < discardTiles.length; i++){
			for(var j = 0; j < waitingTiles.length; j++){
				if(discardTiles[i].isSameTile(waitingTiles[j])){
					return true;
				}
			}
		}
		return false;
	}

	/**
	 * To count how many yaku (Japaness : 役) the winner get
	 * @param {Player} p The winner
	 * @return {ScoreResult|Boolean} The detail of Yakus. Including the name of yakus, han, numbers of yakumans, score point
	 * @see Hand.countMeldsInHand
	 * @see Score-fu
	 */
	this.yakuList = function(p, lastTile){
		if(!(p instanceof Player)) return false;
		if(!lastTile){
			if(p.pickingUpTile){
				lastTile = p.pickingUpTile;
			}else{
				return false;
			}
		}
		var i;
		//MahjongTiles[]
		var agariHand = p.hand.clone(),		//the finish hand with
		    playerHand = p.hand.clone();	//player's hand

		//hand result
		var playerHandWithAgariTile = p.hand.clone();
		playerHandWithAgariTile.push(lastTile);

		var readyHandResults = Hand.countMeldsInHand(p.hand),
		    agariHandResults = Hand.countMeldsInHand(playerHandWithAgariTile);
		for(i = readyHandResults.length-1; i >= 0; i--){
			//check whether it is ready hand
			if(! Hand.isReadyHand(readyHandResults[i]) ){
				readyHandResults.removeByIndex(i);
			}
		}
		for(i = agariHandResults.length-1; i >= 0; i--){
			//check whether it is ready hand
			if(! Hand.isFinishHand(agariHandResults[i]) ){
				agariHandResults.removeByIndex(i);
			}
		}
		// avoid 国士無双 affect checking result
		readyHandResults.sort(function(a, b){
			return b.unused.length - a.unused.length
		});
		agariHandResults.sort(function(a, b){
			return b.unused.length - a.unused.length
		});
		if(agariHandResults.length > 1){
			var hasNormalHand = false;
			for(i = agariHandResults.length-1; i >= 0; i--){
				if(agariHandResults[i].unused.length == 0){
					hasNormalHand = true;
					continue;
				}
				if(hasNormalHand && agariHandResults[i].unused.length > 1){
					agariHandResults.removeByIndex(i);
				}
			}
			if(hasNormalHand && readyHandResults.length > 1){
				for(i = readyHandResults.length-1; i >= 0; i--){
					if(readyHandResults[i].unused.length > 1){
						readyHandResults.removeByIndex(i);
					}
				}
			}
		}


		if(readyHandResults.length == 0 || agariHandResults.length == 0){
			return false;
		}


		//result details
		var yakuList = [],	// the list of the yakus
		    han = 0,		// the number of 飜 player get
		    yakumanList = [],
		    yakumans = 0;	// the number of yakumans


		agariHand.push(lastTile);
		var doorClear = p.isDoorClear;
		var meld_detail = {PONG: 0, KONG: 0, CHOW: 0}
		//To set agariHand as an 和牌
		for(i = 0; i < p.meld.length; i++){
			var m = p.meld[i]
			if(m.type == MeldType.PONG){
				meld_detail.PONG++;
				m.tiles.forEach(function(val,index,arr){
					agariHand.push(val);
				});
			}else if(m.type == MeldType.KONG){
				meld_detail.KONG++;
				agariHand.push(m.tiles[0],m.tiles[1],m.tiles[2]);
			}else if(m.type == MeldType.CHOW){
				meld_detail.CHOW++
				m.tiles.forEach(function(val,index,arr){
					agariHand.push(val);
				});
			}
		}
		Hand.sort(agariHand);
		var check_color = {
			wan: 0,		//萬子
			banboo: 0,	//索
			pin: 0,		//餅
			green: 0,	//綠色牌
			terminals: 0,	//老頭牌
			honors: 0	//字牌
		}

		for(i = 0; i < agariHand.length; i++){
			var m = agariHand[i];
			switch(m.typeCode){
				case MahjongType.Pin:
					check_color.pin += 1;
					break;
				case MahjongType.Wan:
					check_color.wan += 1;
					break;
				case MahjongType.Sou:
					check_color.banboo += 1;
					break;
				case MahjongType.Word:
					check_color.honors += 1;
					break;
			}

			if(m.isGreen){
				check_color.green++;
			}

			if(m.simpleNum == 1 || m.simpleNum == 9){
				check_color.terminals ++;
			}
		}
		//僅有一種數牌
		var only_one_type_of_number_tile = (
			((Boolean(check_color.wan) || Boolean(check_color.banboo) || Boolean(check_color. pin)) == true) &&
			((Boolean(check_color.wan) && Boolean(check_color.banboo) && Boolean(check_color. pin)) == false) &&
			((Boolean(check_color.wan) ^  Boolean(check_color.banboo) ^  Boolean(check_color. pin)) == true)
		)

		/*
		 * 役満
		 * *********************************/
		//天和	テンホウ		Heavenly hand
		if(doorClear && RoundDetail.pickedUpTilesNum == 1){
			yakumanList.push("天和");
			yakumans++;
		}
		//地和	チーホウ		Hand of earth
		if(doorClear &&
		   RoundDetail.pickedUpTilesNum != 1 && RoundDetail.pickedUpTilesNum <= players.length &&
		   (RoundDetail.pickedUpTilesNum - RoundDetail.dealer  + 1 + 4) % 4 == p.id &&
		   lastTile == p.pickingUpTile){
			var canHandOfEarth = true;
			for(i = p.index; i >= 0; i--){
				if(players[i].meld.length != 0){
					canHandOfEarth = false;
				}
			}
			if(canHandOfEarth){
				yakumanList.push("地和");
				yakumans++;
			}
		}
		//四槓子	スーカンツ		Four kans
		if(meld_detail.KONG == 4){
			yakumanList.push("四槓子");
			yakumans++;
		}
		//清老頭	チンロートー		All terminals
		if(check_color.terminals == agariHand.length){
			yakumanList.push("清老頭");
			yakumans++;
		}
		//緑一色	リューイーソー		All green
		if(check_color.green == agariHand.length){
			yakumanList.push("緑一色");
		}
		//字一色	ツーイーソー		All honors
		if(check_color.honors == agariHand.length){
			yakumanList.push("字一色");
			yakumans++;

		}

		//九蓮宝燈	チューレンポウトー	Nine gates
		if(doorClear && p.meld.length == 0 && only_one_type_of_number_tile){
			var agari_result = {"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0};
			playerHandWithAgariTile.forEach(function(val, index, arr){
				agari_result[val.simpleNum]++;
			});
			if(agari_result[1] >= 3 &&
			   agari_result[2] >= 1 &&
			   agari_result[3] >= 1 &&
			   agari_result[4] >= 1 &&
			   agari_result[5] >= 1 &&
			   agari_result[6] >= 1 &&
			   agari_result[7] >= 1 &&
			   agari_result[8] >= 1 &&
			   agari_result[9] >= 3){
				yakumanList.push("九蓮宝燈");
				yakumans++;
			}
		}
		/*
		 * ダブル役満
		 * *********************************/
		if(Rules.Double_Yakuman){
			//純正九蓮宝燈	九蓮寶燈九面待ち
			if(yakumanList.indexOf("九蓮宝燈") >= 0){
				var hand_result = {"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0};
				playerHand.forEach(function(val, index, arr){
					hand_result[val.simpleNum]++;
				});
				if(hand_result[1] == 3 &&
				   hand_result[2] == 1 &&
				   hand_result[3] == 1 &&
				   hand_result[4] == 1 &&
				   hand_result[5] == 1 &&
				   hand_result[6] == 1 &&
				   hand_result[7] == 1 &&
				   hand_result[8] == 1 &&
				   hand_result[9] == 3){
					yakumanList.remove("九蓮宝燈");
					yakumanList.push("純正九蓮宝燈");
					yakumans++;

				}

			}
		}

		/*
		 * 根據牌來決定的役
		 * *********************************/
		/* 六飜 */
		//清一色	チンイーソー	Flush
		if(check_color.honors == 0 && only_one_type_of_number_tile){
			yakuList.push("清一色");
			han += 6
			if(!doorClear)
				han -= 1;	//非門前減飜
		}
		/* 三飜 */
		//混一色	ホンイーソー	Half-flush
		if(check_color.honors != 0 &&only_one_type_of_number_tile){
			yakuList.push("混一色");
			han += 3
			if(!doorClear)
				han -= 1;	//非門前減飜
		}
		/* 一飜 */
		//断么九	タンヤオチュー	All simples
		if(check_color.terminals + check_color.honors == 0){
			yakuList.push("断么九");
			han += 1
		}
		/*
		 * 根據時間決定的役
		 * *********************************/
		/* 二飜 */
		//ダブル立直	ダブルリーチ		Double-ready
		if(p.isDoubleRichi && doorClear){
			yakuList.push("Ｗ立直");
			han += 2;
		}
		/* 一飜 */
		//立直		リーチ			Ready hand
		if(p.isRichi && !p.isDoubleRichi && doorClear){
			yakuList.push("立直");
			han += 1;
		}
		//一発		イッパツ			One-shot
		if(p.canOneShotTurn){
			yakuList.push("一發");
			han += 1;
		}
		//門前清自摸和	メンゼンチンツモホウ	Self-pick
		if(lastTile == p.pickingUpTile && doorClear){
			yakuList.push("自摸");
			han += 1;
		}
		//嶺上開花		リンシャンカイホウ		Dead wall draw
		if(lastTile == p.pickingUpTile && lastTile.isRinshanTile){
			yakuList.push("嶺上開花");
			han += 1;
		}
		//海底摸月／海底撈月		ハイテイモーユエ／ハイテイラオユエ		Last tile from the wall
		if(lastTile == p.pickingUpTile && lastTile.pickingTileNum == bgColor="transparent"){
			yakuList.push("海底撈月");
			han += 1;
		}
		//河底撈魚		ホーテイラオユイ		Last discard
		if(lastTile != p.pickingUpTile && lastTile.pickingTileNum == bgColor="transparent"){
			yakuList.push("河底撈魚");
			han += 1;
		}
		/*
		 * 根據牌型決定的役
		 * *********************************/
		var yakuresult_list = [];
		agariHandResults.forEach(function(val, index, arr){
			var fu = _fu(val, readyHandResults, p.meld, lastTile == p.pickingUpTile, lastTile.isRinshanTile, p.wind);
			var _yakumanList = yakumanList.clone(),
			    _yakuList = yakuList.clone();
			var _yakumans = yakumans,
			    _han = han;
			/*
			 * 役満
			 * *********************************/
			//四暗刻	スーアンコー		Four concealed triplets
			var num_of_concealed_triplets = 0;
			val.concealedTriplets.forEach(function(_val, _index, _arr){
				var allFromHand = true
				_val.forEach(function(__val, __index, __arr){
					if(__val.owner.id != p.id){
						allFromHand = false;
					}
				});
				if(allFromHand){
					num_of_concealed_triplets++;
				}
			});
			p.meld.forEach(function(_val, _index, _arr){
				if(_val.type == MeldType.KONG &&
				   (_val.kongType == KongType.Closed_Quad_from_Hand || _val.kongType == KongType.Closed_Quad_from_PickupTile)){
					num_of_concealed_triplets++;
				}
			});
			if(num_of_concealed_triplets == 4){
				_yakumanList.push("四暗刻");
				_yakumans++;
			}
			//小四喜	ショウスーシー		Little four winds
			var check_word_in_triplets = {"east":0,"west":0,"south":0,"north":0,"haku":0,"hatsu":0,"chun":0},
			    check_word_in_pair = {"east":0,"west":0,"south":0,"north":0,"haku":0,"hatsu":0,"chun":0};
			val.concealedTriplets.forEach(function(_val, _index, _arr){
				if(_val[0].typeCode == MahjongType.Word){
					switch(_val[0].wordNum){
						case MahjongWordNumCode.East:
							check_word_in_triplets.east++;
							break;
						case MahjongWordNumCode.South:
							check_word_in_triplets.west++;
							break;
						case MahjongWordNumCode.West:
							check_word_in_triplets.south++;
							break;
						case MahjongWordNumCode.North:
							check_word_in_triplets.north++;
							break;
						case MahjongWordNumCode.Haku:
							check_word_in_triplets.haku++;
							break;
						case MahjongWordNumCode.Hatsu:
							check_word_in_triplets.hatsu++;
							break;
						case MahjongWordNumCode.Chun:
							check_word_in_triplets.chun++;
							break;
					}
				}
			});
			p.meld.forEach(function(_val, _index, _arr){
				if(_val.type != MeldType.KONG && _val.type != MeldType.PONG){
					return;
				}
				if(_val.takedTile.typeCode == MahjongType.Word){
					switch(_val.takedTile.wordNum){
						case MahjongWordNumCode.East:
							check_word_in_triplets.east++;
							break;
						case MahjongWordNumCode.South:
							check_word_in_triplets.west++;
							break;
						case MahjongWordNumCode.West:
							check_word_in_triplets.south++;
							break;
						case MahjongWordNumCode.North:
							check_word_in_triplets.north++;
							break;
						case MahjongWordNumCode.Haku:
							check_word_in_triplets.haku++;
							break;
						case MahjongWordNumCode.Hatsu:
							check_word_in_triplets.hatsu++;
							break;
						case MahjongWordNumCode.Chun:
							check_word_in_triplets.chun++;
							break;
					}
				}
			});
			if(val.pairs[0][0].typeCode == MahjongType.Word){
				switch(val.pairs[0][0].wordNum){
					case MahjongWordNumCode.East:
						check_word_in_pair.east++;
						break;
					case MahjongWordNumCode.South:
						check_word_in_pair.west++;
						break;
					case MahjongWordNumCode.West:
						check_word_in_pair.south++;
						break;
					case MahjongWordNumCode.North:
						check_word_in_pair.north++;
						break;
					case MahjongWordNumCode.Haku:
						check_word_in_pair.haku++;
						break;
					case MahjongWordNumCode.Hatsu:
						check_word_in_pair.hatsu++;
						break;
					case MahjongWordNumCode.Chun:
						check_word_in_pair.chun++;
						break;
				}
			}

			if(check_word_in_triplets.east + check_word_in_triplets.south + check_word_in_triplets.west + check_word_in_triplets.north >= 3 &&
			   check_word_in_triplets.east + check_word_in_triplets.south + check_word_in_triplets.west + check_word_in_triplets.north + check_word_in_pair.east + check_word_in_pair.south + check_word_in_pair.west + check_word_in_pair.north == 4 ){
				_yakumanList.push("小四喜");
				_yakumans++;
			}

			//大三元	ダイサンゲン		Big three dragons
			if(check_word_in_triplets.haku && check_word_in_triplets.hatsu && check_word_in_triplets.chun){
				_yakumanList.push("大三元");
				_yakumans++;
			}
			//国士無双	コクシムソウ		Thirteen orphans
			if(doorClear && val.unused.length == 12 && val.pairs){
				_yakumanList.push("国士無双");
				_yakumans++;
			}
			/*
			 * W役満
			 * *********************************/
			if(Rules.Double_Yakuman){
				//四暗刻単騎	スーアンコーたんき	Four concealed triplets single wait
				if(_yakumanList.indexOf("四暗刻") >= 0){
					var isSingleWait = false;
					readyHandResults.forEach(function(_val, _index, _arr){
						if(_val.unused.length == 1 && _val.pairs.length == 0 && _val.straight.length == 0){
							isSingleWait = true;
						}
					});
					if(isSingleWait){
						_yakumanList.remove("四暗刻");
						_yakumanList.push("四暗刻単騎");
						_yakumans++;
					}
				}
				//大四喜		ダイスーシー	Big four winds
				if(_yakumanList.indexOf("小四喜") >= 0){
					if(check_word_in_triplets.east + check_word_in_triplets.south + check_word_in_triplets.west + check_word_in_triplets.north == 4){
						_yakumanList.remove("小四喜");
						_yakumanList.push("大四喜");
						_yakumans++;
					}
				}
				//国士無双十三面待ち	國士無雙十三面聽	Thirteen orphans 13 wait
				if(_yakumanList.indexOf("国士無双") >= 0){
					var isRisingSun = false;
					for(i = 0; i < readyHandResults.length; i++){
						var readyHandResult = readyHandResults[i];
						if(isRisingSun){
							break;
						}
						if(readyHandResult.concealedTriplets.length >0 ||
						   readyHandResult.straight.length > 0 ||
						   readyHandResult.pairs.length > 0){
							isRisingSun = false;
							break
						}
						if(readyHandResult.unused.length == 13){
							isRisingSun = true;
						}
					}

					if(isRisingSun){
						_yakumanList.remove("国士無双");
						_yakumanList.push("国士無双十三面待ち");
						_yakumans++;
					}
				}
			}
			/*
			 * 普通役
			 * *********************************/
			if(Rules.Aotenjou || _yakumanList.length == 0 || _yakumans == 0){
				/* 三飜 */
				//二盃口	リャンペーコー	Two sets of identical sequences
				var num_of_sets = 0;
				val.straight.sort(function(a, b){
					Hand.sort(a);
					Hand.sort(b);
					if(a[0].typeCode != b[0].typeCode){
						return a[0].typeCode - b[0].typeCode;
					}else if(a[0].simpleNum != b[0].simpleNum){
						return a[0].simpleNum - b[0].simpleNum;
					}
				});
				for(i = 0; i < val.straight.length - 1; ){
					if(val.straight[i][0].isSameTile(val.straight[i+1][0])){
						num_of_sets++;
						i+=2;
					}else{
						i++;
					}
				}
				if(doorClear && num_of_sets == 2){
					_yakuList.push("二盃口");
					_han += 3;
				}
				/* 二飜 */
				//七対子	チートイツ	7 pairs
				if(doorClear && val.pairs.length == 7){
					_yakuList.push("七対子");
					_han += 2;
				}
				//対々和	トイトイホー		All triplet hand
				if(meld_detail.CHOW == 0 && val.straight.length == 0 && val.concealedTriplets.length + p.meld.length == 4){
					_yakuList.push("対々和");
					_han += 2;
				}
				//三暗刻	サンアンコー		Three closed triplets
				if(num_of_concealed_triplets == 3){
					_yakuList.push("三暗刻");
					_han += 2;
				}
				//三槓子	サンカンツ		Three kans
				if(meld_detail.KONG == 4){
					_yakuList.push("三槓子");
					_han += 2;
				}
				/* 一飜 */
				//平和	ピンフ	No-points hand
				if(doorClear &&
				   p.meld.length == 0 &&
				   val.straight.length == 4 &&
				   fu == 20 + (lastTile == p.pickingUpTile? 0: 10)){
					var whether_no_points_hand = false;
					val.straight.forEach(function(_val, _arr){
						if(whether_no_points_hand)
							return;
						Hand.sort(_val);
						if(_val.indexOf(lastTile) >= 0 &&
						   (_val.indexOf(lastTile) == 0 || _val.indexOf(lastTile) == _val.length - 1)){
							if(!(_val[0].simpleNum == 1 && _val.indexOf(lastTile) == _val.length - 1) &&
							   !(_val[0].simpleNum == 7 && _val.indexOf(lastTile) == 0)){
								whether_no_points_hand = true;
							}
						}
					});
					if(whether_no_points_hand){
						_yakuList.push("平和");
						_han += 1;
					}
				}
				//一盃口	イーペーコー	One set of identical sequences
				if(doorClear && num_of_sets == 1){
					_yakuList.push("一盃口");
					_han += 1;
				}
				/*
				 * 混合
				 * *********************************/
				/* 三飜 */
				//純全帯么九	ジュンチャンタイヤオチュー	Terminal in each set
				var num_of_sets_which_have_terminal_tile = 0,
				    num_of_sets_which_have_honor_tile = 0;
				p.meld.forEach(function(_val, _index, _arr){
					switch(_val.type){
						case MeldType.KONG:
						case MeldType.PONG:
							if(_val.takedTile.isYaoChuuTile){
								if(_val.takedTile.typeCode == MahjongType.Word){
									num_of_sets_which_have_honor_tile++;
								}else{
									num_of_sets_which_have_terminal_tile++;
								}
							}
							break;
						case MeldType.CHOW:
							_val.takedTile.tiles.forEach(function(__val, __index, __arr){
								if(__val.isYaoChuuTile){
									num_of_sets_which_have_terminal_tile++;
								}
							});
							break;
						default:
							break;
					}
				});
				val.pairs.forEach(function(_val, _index, _arr){
					if(_val[0].isYaoChuuTile){
						if(_val[0].typeCode == MahjongType.Word){
							num_of_sets_which_have_honor_tile++;
						}else{
							num_of_sets_which_have_terminal_tile++;
						}
					}
				});
				val.concealedTriplets.forEach(function(_val, _index, _arr){
					if(_val[0].isYaoChuuTile){
						if(_val[0].typeCode == MahjongType.Word){
							num_of_sets_which_have_honor_tile++;
						}else{
							num_of_sets_which_have_terminal_tile++;
						}
					}
				});
				val.straight.forEach(function(_val, _index, _arr){
					_val.forEach(function(__val, __index, __arr){
						if(__val.isYaoChuuTile){
							num_of_sets_which_have_terminal_tile++;
						}
					});
				});
				if(num_of_sets_which_have_terminal_tile == 5 &&
				   num_of_sets_which_have_honor_tile == 0 &&
				   val.straight.length + meld_detail.CHOW > 0){
					_yakuList.push("純全帯么九");
					_han += 3;
					if(!doorClear)
						han -= 1;	//非門前減飜
				}
				/* 二飜 */
				//混老頭		ホンロウトー	All terminals and honors
				if(val.unused.length == 0 &&	//avoid 国士無双
				   check_color.terminals + check_color.honors  == agariHand.length &&
				   check_color.terminals != 0 && check_color.honors != 0){
					_yakuList.push("混老頭");
					_han += 2;
				}
				//混全帯么九	ホンチャンタイヤオチュー	Terminal or honor in each set
				if(num_of_sets_which_have_terminal_tile + num_of_sets_which_have_honor_tile == 5 &&
				   num_of_sets_which_have_terminal_tile != 0 &&
				   num_of_sets_which_have_honor_tile != 0 &&
				   val.straight.length + meld_detail.CHOW > 0){
					_yakuList.push("混全帯么九");
					_han += 2;
					if(!doorClear)
						han -= 1;	//非門前減飜
				}
				//小三元		ショウサンゲン	Little three dragons
				if(check_word_in_triplets.haku + check_word_in_triplets.hatsu + check_word_in_triplets.chun == 2 &&
				   check_word_in_pair.haku + check_word_in_pair.hatsu + check_word_in_pair.chun == 1){
					_yakuList.push("小三元");
					_han += 2;
				}
				//三色同順	サンショクドウジュン	Three colour straight
				var straight_detail = {}
				val.straight.forEach(function(_val, _index, _arr){
					Hand.sort(_val);
					if(!straight_detail[_val[0].simpleNum]){
						straight_detail[_val[0].simpleNum] = {};
					}
					switch(_val[0].typeCode){
						case MahjongType.Pin:
							straight_detail[_val[0].simpleNum].Pin = true;
							break;
						case MahjongType.Wan:
							straight_detail[_val[0].simpleNum].Wan = true;
							break;
						case MahjongType.Sou:
							straight_detail[_val[0].simpleNum].Sou = true;
							break;
					}
				});
				p.meld.forEach(function(_val, _index, _arr){
					if(_val.type != MeldType.CHOW)
						return;
					if(!straight_detail[_val.tiles[0].simpleNum]){
						straight_detail[_val.tiles[0].simpleNum] = {};
					}
					switch(_val.tiles[0].typeCode){
						case MahjongType.Pin:
							straight_detail[_val.tiles[0].simpleNum].Pin = true;
							break;
						case MahjongType.Wan:
							straight_detail[_val.tiles[0].simpleNum].Wan = true;
							break;
						case MahjongType.Sou:
							straight_detail[_val.tiles[0].simpleNum].Sou = true;
							break;
					}
				});
				for(i in straight_detail){
					if(straight_detail[i].Pin && straight_detail[i].Wan && straight_detail[i].Sou){
						_yakuList.push("三色同順");
						_han += 2;
						if(!doorClear)
							han -= 1;	//非門前減飜
						break;
					}
				}
				//一気通貫	イッキツウカン		Straight
				if(straight_detail[1] && straight_detail[4] && straight_detail[7] &&
				   ((straight_detail[1].Pin && straight_detail[4].Pin && straight_detail[7].Pin) ||
				    (straight_detail[1].Pin && straight_detail[4].Pin && straight_detail[7].Pin) ||
				    (straight_detail[1].Pin && straight_detail[4].Pin && straight_detail[7].Pin))
				  ){
					_yakuList.push("一気通貫");
					_han += 2;
					if(!doorClear)
						han -= 1;	//非門前減飜
				}
				//三色同刻	サンショクドーコー		Three colour triplets
				var triplets_detail = {}
				val.concealedTriplets.forEach(function(_val, _index, _arr){
					if(!triplets_detail[_val[0].simpleNum]){
						triplets_detail[_val[0].simpleNum] = {};
					}
					switch(_val[0].typeCode){
						case MahjongType.Pin:
							triplets_detail[_val[0].simpleNum].Pin = true;
							break;
						case MahjongType.Wan:
							triplets_detail[_val[0].simpleNum].Wan = true;
							break;
						case MahjongType.Sou:
							triplets_detail[_val[0].simpleNum].Sou = true;
							break;
					}
				});
				p.meld.forEach(function(_val, _index, _arr){
					if(_val.type == MeldType.CHOW || _val.takedTile.typeCode == MahjongType.Word)
						return;
					if(!triplets_detail[_val.takedTile.simpleNum]){
						triplets_detail[_val.takedTile.simpleNum] = {};
					}
					switch(_val.takedTile.typeCode){
						case MahjongType.Pin:
							triplets_detail[_val.takedTile.simpleNum].Pin = true;
							break;
						case MahjongType.Wan:
							triplets_detail[_val.takedTile.simpleNum].Wan = true;
							break;
						case MahjongType.Sou:
							triplets_detail[_val.takedTile.simpleNum].Sou = true;
							break;
					}
				});
				for(i in triplets_detail){
					if(triplets_detail[i].Pin && triplets_detail[i].Wan && triplets_detail[i].Sou){
						_yakuList.push("三色同刻");
						_han += 2;
					}
				}
				/* 一飜 */
				//槍槓		チャンカン		Robbing a quad
				if(lastTile != p.pickingUpTile){
					var canRobbingAQuad
					players[lastTile.owner.id].meld.forEach(function(val, index, arr){
						if(val.type == MeldType.KONG && val.takedTile.isSameTile(lastTile)){
							if(val.kongType == KongType.Added_Open_Quad){
								canRobbingAQuad = true;
							}else if(doorClear && val.unused.length == 12 && val.pairs){
								// if it is 國士無雙， even if it is a closed Quad, it still can be robbed
								canRobbingAQuad = true;
							}
						}
					});
					if(canRobbingAQuad){
						_yakuList.push("槍槓");
						_han += 1;
					}
				}
				//役牌・飜牌・翻牌	ヤクハイ、ファンパイ、ハンパイ	Honor tiles
				if(check_word_in_triplets.haku){
					_yakuList.push("役牌・白");
					_han += check_word_in_triplets.haku;
				}
				if(check_word_in_triplets.hatsu){
					_yakuList.push("役牌・発");
					_han += check_word_in_triplets.hatsu;
				}
				if(check_word_in_triplets.chun){
					_yakuList.push("役牌・中");
					_han += check_word_in_triplets.chun;
				}
				if(check_word_in_triplets.east){
					if(RoundDetail.code == MahjongWordNumCode.East){
						_yakuList.push("場風・東");
						_han += check_word_in_triplets.east;
					}
					if(p.wind == MahjongWordNumCode.East){
						_yakuList.push("自風・東");
						_han += check_word_in_triplets.east;
					}
				}
				if(check_word_in_triplets.south){
					if(RoundDetail.code == MahjongWordNumCode.South){
						_yakuList.push("場風・南");
						_han += check_word_in_triplets.south;
					}
					if(p.wind == MahjongWordNumCode.South){
						_yakuList.push("自風・南");
						_han += check_word_in_triplets.south;
					}
				}
				if(check_word_in_triplets.west){
					if(RoundDetail.code == MahjongWordNumCode.West){
						_yakuList.push("場風・西");
						_han += check_word_in_triplets.west;
					}
					if(p.wind == MahjongWordNumCode.South){
						_yakuList.push("自風・西");
						_han += check_word_in_triplets.west;
					}
				}
				if(check_word_in_triplets.north){
					if(RoundDetail.code == MahjongWordNumCode.North){
						_yakuList.push("場風・北");
						_han += check_word_in_triplets.north;
					}
					if(p.wind == MahjongWordNumCode.South){
						_yakuList.push("自風・北");
						_han += check_word_in_triplets.north;
					}
				}
			}
			var score;
			if(Rules.Aotenjou){
				score = Score.scorePoint(fu, _han + _yakumans * 13 );
			}else if(_yakumans > 0){
				score = _yakumans * 8000;
			}else{
				score = Score.scorePoint(fu, _han);
			}

			yakuresult_list.push({
				yakumanList: _yakumanList,
				num_of_yakumans: _yakumans,
				yakuList: _yakuList,
				han: _han,
				fu: fu,
				score: score,
				handinfo: val
			});
		});

		yakuresult_list.sort(function(a,b){
			return b.score - a.score;
		});
		yakumanList = yakuresult_list[0].yakumanList;
		yakumans = yakuresult_list[0].num_of_yakumans;
		yakuList = yakuresult_list[0].yakuList;
		han = yakuresult_list[0].han;
		var fu = yakuresult_list[0].fu;

		/*
		 * 懸賞牌・ドラ
		 * *********************************/
		if(han || (Rules.Aotenjou && (yakumans || han))){
			var num_of_doras = 0,
			    num_of_RedDoras = 0;

			var doras = Dora.getDoras(Dora.doraIndicators);
			var uraDoras = [];
			if(doorClear){
				uraDoras = Dora.getDoras(Dora.uraDoraIndicators);
			}
			var checkIsDora = function(tile){
				//表ドラ／槓ドラ	Dora
				doras.forEach(function(_val, _index, _arr){
					if(_val.isSameTile(tile)){
						num_of_doras++;
					}
				});
				//裏ドラ／槓ウラ
				if(doorClear){
					uraDoras.forEach(function(_val, _index, _arr){
						if(_val.isSameTile(tile)){
							num_of_doras++;
						}
					});
				}
				//赤ドラ
				if(tile.isRedDora){
					num_of_RedDoras++;
				}
			}

			p.hand.forEach(function(val, index, arr){
				checkIsDora(val);
			});
			checkIsDora(lastTile);
			p.meld.forEach(function(val, index, arr){
				checkIsDora(val.takedTile);
				val.meldTiles.forEach(function(_val, _index, _arr){
					checkIsDora(_val);
				});

				if(val.addKongTile){
					checkIsDora(val.addKongTile);
				}
			});

			if(num_of_doras > 0){
				yakuList.push("ドラ\t"+num_of_doras);
				han += num_of_doras;
			}
			if(num_of_RedDoras > 0){
				yakuList.push("赤ドラ\t"+num_of_RedDoras);
				han += num_of_RedDoras;
			}
		}
		return ScoreResult(yakumanList, yakumans, yakuList, han, fu)
	}
}
/**
 * @class
 * @param {String[]}	yakumans
 * @param {Number}	numOfYakumans
 * @param {String[]}	yakus
 * @param {Number}	han
 * @param {Number}	fu
 */
function ScoreResult(yakumans, numOfYakumans, yakus, han, fu){
	if(!(this instanceof ScoreResult)){
		return new ScoreResult(yakumans, numOfYakumans, yakus, han, fu);
	}
	if(!numOfYakumans && !han){
		this.yakus = yakus || [];
		return false;
	}
	if(Rules.Aotenjou){
		var _yakus = yakumans.clone();
		yakus.forEach(function(val, index, arr){
			_yakus.push(val);
		});

		this.yakus = _yakus;
		this.numOfYakumans = numOfYakumans;
		this.han = numOfYakumans * 13 + han;
		this.fu = fu;
		this.score = Score.scorePoint(fu, numOfYakumans * 13 + han);
	}else{
		if(yakumans.length > 0 && numOfYakumans > 0){
			this.yakus = yakumans;
			this.numOfYakumans = numOfYakumans;
			this.han = numOfYakumans * 13 + han;
			this.fu = fu;
			this.score = numOfYakumans * 8000;
		}else{
			this.yakus = yakus;
			this.numOfYakumans = numOfYakumans;
			this.han = han;
			this.fu = fu;
			this.score = Score.scorePoint(fu, han);
		}
	}
}
/**
 * The list of Yakus. If the rule support Aotenjo, it include Yakemans and normal Yakus.
 * @type String[]
 * @default null
 */
ScoreResult.prototype.yakus = null;
/**
 * The multiple of Yakumans.
 * @default 0
 * @type Number
 */
ScoreResult.prototype.numOfYakumans = 0;
/**
 * @see Score.scorePoint
 * @default 0
 * @type Number
 */
ScoreResult.prototype.score = 0;
/**
 * 符
 * @see fu
 * @see ScoreResult#han
 * @type Number
 */
ScoreResult.prototype.fu = 0;
/**
 * 飜
 * @see ScoreResult#fu
 * @type Number
 */
ScoreResult.prototype.han = 0;